from google.appengine.ext import db

from gaev import exceptions
from gaev.model import BaseModel


class FakeQuery(object):
	def __init__(self, keys):
		self.keys=keys;
	def __iter__(self):
		if not self.entities:
			self.entities=db.get(self.keys)
		return self.entities
	def filter(self, property_operator, value):
		raise exceptions.NotImplementedError
	def order(self, property):
		pass
	def ancestor(self, ancestor):
		pass
	def get(self):
		if not len(self.keys):
			return None
		return db.get(self.keys[0])
	def fetch(self, limit, offset=0):
		fetch_keys=self.keys[offset:offset+limit]
		return db.get(fetch_keys)
	def count(self, limit):
		return min(limit, len(self.keys))
	
class Restrictor(object):
	def __init__(self, entity_set):
		self.entity_set=entity_set
	def get_all_query(self):
		raise exceptions.NotImplementedError
	def get_specifier(self):
		raise exceptions.NotImplementedError
	def get_restricted_entity_set(self):
		import copy
		import new
		r_es=copy.copy(self.entity_set)

		r_es.all=new.instancemethod(lambda obj:self.get_all_query(), r_es, r_es.__class__)
		
		r_es.restrictor=self
		return r_es
		
	@classmethod
	def from_specifier(cls, entity_set, specifier):
		raise exceptions.NotImplementedError
	@classmethod
	def parse_specifier(cls, entity_set, specifier):
		classes=[NullRestrictor, ReferenceRestrictor, ListRestrictor]
		for rcls in classes:
			res=rcls.from_specifier(entity_set, specifier)
			if res:
				return res
		raise exceptions.InvalidRestrictorError(specifier)
	

class NullRestrictor(Restrictor):
	def get_all_query(self):
		return self.entity_set.all()
	def get_specifier(self):
		return ''
	def get_restricted_model(self):
		return self.entity_set
	@classmethod
	def from_specifier(cls, entity_set, specifier):
		if not specifier:
			return NullRestrictor(entity_set)
		else:
			return None

class ReferenceRestrictor(Restrictor):
	def __init__(self, entity_set, property, entity):
		super(ReferenceRestrictor, self).__init__(entity_set)
		if isinstance(property, str):
			self.property_name=property
		else:
			self.property_name=property.name
		if isinstance(entity, str):
			self.key=entity
		elif entity.get_human_key():
			self.key=getattr(entity, entity.get_human_key())
		else:
			self.key=entity.key()
			
	def get_all_query(self):
		query=self.entity_set.all()
		property=self.entity_set.model.properties()[self.property_name]
		
		if isinstance(property, db.ReferenceProperty):
			target_model=property.reference_class
		elif isinstance(property, db.ListProperty) and hasattr(property, '_target'):
			target_model=property._target
		else:
			raise exceptions.ModelError("Specified property '%s' is neither reference nor list." % self.property_name)
		if target_model.get_human_key():
			target=target_model.entities().get_by_human_key(self.key)
		else:
			target=target_model.entities().get(self.key)
			
		cond="%s = " % self.property_name
		query.filter(cond, target)
		return query
	def get_specifier(self):
		return "rf@%s@%s" % (self.property_name, self.key)
	@classmethod
	def from_specifier(cls, entity_set, specifier):
		import re
		match_obj=re.compile('^rf@([^@]*)@(.*)$').match(specifier)
		if match_obj:
			property_name=match_obj.group(1)
			key=match_obj.group(2)
			return ReferenceRestrictor(entity_set, property_name, key)
		else:
			return None

class ListRestrictor(Restrictor):
	def __init__(self, entity_set, entity, property):
		super(ListRestrictor, self).__init__(entity_set)
		if isinstance(property, str):
			self.property_name=property
		else:
			self.property_name=property.name
		if isinstance(entity, str):
			self.key=entity
		else:
			self.key=entity.key()
	def get_all_query(self):
		entity=db.get(self.key)
		list=getattr(entity, self.property_name)
		return FakeQuery(list)
	def get_specifier(self):
		return "ls@%s@%s" % (self.key, self.property_name)
	@classmethod
	def from_specifier(cls, entity_set, specifier):
		import re
		match_obj=re.compile('^ls@([^@]*)@(.*)$').match(specifier)
		if match_obj:
			key=match_obj.group(1)
			property_name=match_obj.group(2)
			return ListRestrictor(entity_set, key, property_name)
		else:
			return None
	

def get_restricted_entity_set(entity_set, specifier):
	restrictor=Restrictor.parse_specifier(entity_set, specifier)
	return restrictor.get_restricted_entity_set()
	

	